#include <cmath>
#include <iostream>

#include "point.h"

#define ERROR 0.00001

Point::Point(){
	this->x = 0.0;
	this->y = 0.0;
	this->z = 0.0;
}

Point::Point(double x, double y, double z){
	this->x = x;
	this->y = y;
	this->z = z;
}

Point::Point(const Point& v){
	this->x = v.x;
	this->y = v.y;
	this->z = v.z;
}

Point::~Point(){
}

Point Point::operator-(const Point& _v){
	return Point(this->x - _v.x, this->y - _v.y, this->z - _v.z);
}

Point Point::operator+(const Point& _v){
	return Point(this->x + _v.x, this->y + _v.y, this->z + _v.z);
}

Point Point::operator*(double _lambda){
	return Point(this->x*_lambda, this->y*_lambda, this->z*_lambda);
}

bool Point::operator==(const Point& _v){
    if((x == _v.x) && (y == _v.y) && (z == _v.z)) return true;
	else return false;
}

double Point::length(){
	return sqrt(this->dotProduct(*this));
}

Point Point::normalization(){
	double norm = this->length();
	return Point(this->x/norm, this->y/norm, this->z/norm);
}

double Point::dotProduct(Point _v){
	return (this->x*_v.x) + (this->y*_v.y) + (this->z*_v.z);
}

double Point::scalarTripleProduct(Point _v1, Point _v2){
	return this->dotProduct(_v1.crossProduct(_v2));
}

Point Point::crossProduct(Point _v){
	return Point(this->y*_v.z - this->z*_v.y, this->z*_v.x - this->x*_v.z, this->x*_v.y - this->y*_v.x);
}

void Point::print() const{
	std::cout << "coord (  " << x << " ,  " << y << " ,  " << z << "  )" << std::endl;
}

double *Point::intoVector(){
    double *d;
    d = new double[3];
    d[0] = x;
    d[1] = y;
    d[2] = z;

    return d;
}


double solidAngle(Point _a, Point _b, Point _c, Point _p){
    Point a = _a - _p;
    Point b = _b - _p;
    Point c = _c - _p;


    double det = fabs(a.scalarTripleProduct(b,c));

    double aLength = a.length();
    double bLength = b.length();
    double cLength = c.length();

    double div = aLength*bLength*cLength + a.dotProduct(b)*cLength + a.dotProduct(c)*bLength + b.dotProduct(c)*aLength;
    double at = atan2(det, div);

    if(at < 0) at += M_PI;

    double omega = 2.0 * at;

    return omega;

}

bool isCollinear(Point _p1, Point _p2){ //tem que receber os vetores normalizados!
    double *p1 = _p1.intoVector();
    double *p2 = _p2.intoVector();

    bool same = true, opposite = true;
    for(int i = 0; i < 3; i++){
        if (!((p1[i] - ERROR <= p2[i]) && (p1[i] + ERROR >= p2[i])))
            same = false;
        if (!((p1[i] - ERROR <= -p2[i]) && (p1[i] + ERROR >= -p2[i])))
            opposite = false;
    }
    delete p1;
    delete p2;

    return same || opposite;
}

bool isCoplanar(Point _a, Point _b, Point _c, Point _p){
    //testando se os vetores são colineares
    Point ab = (_b - _a).normalization();
    Point ap = (_p - _a).normalization();

    if(isCollinear(ab, ap)) return true;

    Point n = ab.crossProduct((_c - _a).normalization());
    Point np = ab.crossProduct(ap);

    double cosAlpha = fabs(n.dotProduct(np));
//    std::cout << "Se der error estranhsopode ser a função coplanar!!" <<std::endl;

    if( cosAlpha - ERROR <= 1.0 && cosAlpha + ERROR >= 1.0 ) return true; //igual -1 ou 1

    return false;
}
